package com.lesserpand.springcloudalibabacore.config.rabbitmq.config;

import com.lesserpand.springcloudalibabacore.config.rabbitmq.callback.MsgSendConfirmCallBack;
import com.lesserpand.springcloudalibabacore.config.rabbitmq.callback.MsgSendReturnCallBack;
import com.lesserpand.springcloudalibabacore.config.rabbitmq.constant.MqEnum;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * RabbitMq配置
 *
 * @author pand
 */
@Configuration
public class RabbitMqConfig {


    @Autowired
    private QueueConfig queueConfig;

    /**
     * 连接工厂
     */
    @Autowired
    private ConnectionFactory connectionFactory;


    /**
     * 消息中心实际消息交换器与队列绑定，第一次
     *
     * @param firstDirectExchange 消息中心交换配置
     * @param firstMessageQueue   消息中心队列
     * @return
     */
    @Bean
    public Binding messageBinding(DirectExchange firstDirectExchange, Queue firstMessageQueue) {
        return BindingBuilder.bind(firstMessageQueue).to(firstDirectExchange).with(MqEnum.FIRST_MESSAGE_QUEUE_KEY.getValue());
    }

    /**
     * 消息中心实际消息交换器与队列绑定，第二次
     *
     * @param secondDirectExchange 消息中心交换配置
     * @param secondMessageQueue   消息中心队列
     * @return
     */
    @Bean
    public Binding messageBinding2(DirectExchange secondDirectExchange, Queue secondMessageQueue) {
        return BindingBuilder.bind(secondMessageQueue).to(secondDirectExchange).with(MqEnum.SECOND_MESSAGE_QUEUE_KEY.getValue());
    }

    /**
     * 延迟队列1与私信队列通过MESSAGE_TTL_QUEUE_KEY绑定
     *
     * @param firstMessageTtlQueue
     * @param deadLetterExchange
     * @return
     */
    @Bean
    public Binding messageTtlBinding(DirectExchange deadLetterExchange, Queue firstMessageTtlQueue) {
        return BindingBuilder.bind(firstMessageTtlQueue).to(deadLetterExchange).with(MqEnum.FIRST_MESSAGE_TTL_QUEUE_KEY.getValue());
    }


    /**
     * 延迟队列2与私信队列通过MESSAGE_TTL_QUEUE2_KEY绑定
     *
     * @param deadLetterExchange
     * @param secondMessageTtlQueue
     * @return
     */
    @Bean
    public Binding messageTtlBinding2(DirectExchange deadLetterExchange, Queue secondMessageTtlQueue) {
        return BindingBuilder.bind(secondMessageTtlQueue).to(deadLetterExchange).with(MqEnum.SECOND_MESSAGE_TTL_QUEUE_KEY.getValue());
    }

    /**
     * 抢购消息队列和交换机进行绑定
     */
    @Bean
    public Binding skillBinding(DirectExchange skillExchange, Queue skillMessageQueue) {
        return BindingBuilder.bind(skillMessageQueue).to(skillExchange).with(MqEnum.SKILL_MESSAGE_QUEUE_KEY.getValue());
    }

    /**
     * queue listener  观察 监听模式
     * 当有消息到达时会通知监听在对应的队列上的监听对象
     *
     * @return
     */
    @Bean
    public SimpleMessageListenerContainer simpleMessageListenerContainer_one() {
        SimpleMessageListenerContainer simpleMessageListenerContainer = new SimpleMessageListenerContainer(connectionFactory);
        // simpleMessageListenerContainer.addQueues(queueConfig.firstQueue());
        simpleMessageListenerContainer.setExposeListenerChannel(true);
        simpleMessageListenerContainer.setMaxConcurrentConsumers(5);
        simpleMessageListenerContainer.setConcurrentConsumers(1);
        return simpleMessageListenerContainer;
    }

    /**
     * 定义rabbit template用于数据的接收和发送
     *
     * @return
     */
    @Bean
    public RabbitTemplate rabbitTemplate() {
        RabbitTemplate template = new RabbitTemplate(connectionFactory);
        /**若使用confirm-callback或return-callback，
         * 必须要配置publisherConfirms或publisherReturns为true
         * 每个rabbitTemplate只能有一个confirm-callback和return-callback
         */
        template.setConfirmCallback(msgSendConfirmCallBack());
        /**
         * 使用return-callback时必须设置mandatory为true，或者在配置中设置mandatory-expression的值为true，
         * 可针对每次请求的消息去确定’mandatory’的boolean值，
         * 只能在提供’return -callback’时使用，与mandatory互斥
         */
        template.setMandatory(true);
        template.setReturnCallback(msgSendReturnCallBack());
        return template;
    }

    /**
     * 消息确认机制
     * Confirms给客户端一种轻量级的方式，能够跟踪哪些消息被broker处理，
     * 哪些可能因为broker宕掉或者网络失败的情况而重新发布。
     * 确认并且保证消息被送达，提供了两种方式：发布确认和事务。(两者不可同时使用)
     * 在channel为事务时，不可引入确认模式；同样channel为确认模式下，不可使用事务。
     *
     * @return
     */
    @Bean
    public MsgSendConfirmCallBack msgSendConfirmCallBack() {
        return new MsgSendConfirmCallBack();
    }

    @Bean
    public MsgSendReturnCallBack msgSendReturnCallBack() {
        return new MsgSendReturnCallBack();
    }

}
